<?php

namespace Cframe\Tpllib;

use Cframe\Config;

class Tpl{
    protected $config = array();
    protected $tpl_path = '';
    protected $cache_path = '';
    private $last_var = '';
    protected $tplVar = '\$(.*?)';
    protected $tplFuc = '@(.*?)';
    protected $class = '';
    protected $tag = array(
        "load"=>array("alise"=>"import,js,css","close"=>1,"attr"=>'file,href'),
        'url'=>array("close"=>1,"attr"=>'domain,path'),
        'else'=>array("close"=>1),
        'elseif'=>array("close"=>1,"attr"=>'condition'),
        'if'=>array("close"=>0,"attr"=>'condition'),
        'loop'=>array("close"=>0,"attr"=>'name,id,num'),
        'cif'=>array("close"=>0,"attr"=>'name,var'),
        'cvif'=>array("close"=>1,"attr"=>'name,var,default'),
        'include'=>array("close"=>0,"attr"=>'file'),
    );

    public function __construct(){
        $this->config = Config::get("template");
        $this->tpl_path = CON_PATH . "Tpl" . DS;
        //$this->tpl_path = str_replace(APP_PATH,'./',$this->tpl_path);
        $this->cache_path = RUNTIME_PATH . "Tpl" . DS;
        if (!file_exists($this->cache_path) && !mkdir($this->cache_path,0777,true)){
            throw new \Exception("Create Folder Fail");
        }
    }

    public function parseGlobalVar(&$content){
        $tag = include COMMON_PATH . 'Global.php';
        $content = str_replace(array_keys($tag),array_values($tag),$content);
    }

    public function parseVar(&$content){
        $end = $this->quote1($this->config["var_end"]);
        $start = $this->quote1($this->config["var_start"]);
        $reg = "/".$start.$this->tplVar.$end."/";
        if (preg_match($reg,$content)) {
            $content = preg_replace_callback($reg, function ($matchs) {
                $tpl = isset($matchs[1]) ? $matchs[1] : '';
                if (!empty($tpl)) {
                    if (strpos($tpl, '.')) {
                        $tpl = explode(".", $tpl);
                        $var = "$" . array_shift($tpl);
                    } else {
                        $var = "$" . $tpl;
                    }
                    if (!empty($tpl)) {
                        foreach ($tpl as $k) {
                            if (is_numeric($k)) {
                                $var .= "[{$k}]";
                            } else {
                                $var .= "['{$k}']";
                            }

                        }
                    }
                    return "<?php if (isset(".$var.")): echo " . $var . ";endif; ?>";
                }
            }, $content);
        }
    }

    public function parseFuction(&$content){
        $end = $this->quote1($this->config["var_end"]);
        $start = $this->quote1($this->config["var_start"]);
        $reg = "/".$start.$this->tplFuc.$end."/";
        if (preg_match($reg,$content)) {
            $content = preg_replace_callback($reg, function ($matchs) {
                $tpl = isset($matchs[1]) ? $matchs[1] : '';
                if (!empty($tpl)) {
                    return "<?php echo " . $tpl . "; ?>";
                }
            }, $content);
        }
    }

    public function getTagconfig($lib){
        foreach ($this->tag as $k=>$v){
            if (strcmp($lib,$k) == 0){
                $lib = 'tag'.ucfirst($k);
                break;
            }
            if (isset($v['alise'])){
                foreach (explode(',',$v["alise"]) as $m){
                    if (strcmp($lib,$m) == 0){
                        $lib = 'tpl'.ucfirst($k);
                        break;
                    }
                }
            }
        }
        return $lib;
    }

    private function quote1($str) {
        return str_replace(
            ['{', '}', '(', ')', '|', '[', ']', '-', '+', '*', '.', '^', '?'],
            ['\{', '\}', '\(', '\)', '\|', '\[', '\]', '\-', '\+', '\*', '\.', '\^', '\?'],
            $str);
    }

    private function getRex($close = 1,$name = ''){
        $end = $this->quote1($this->config["tag_end"]);
        $start = $this->quote1($this->config["tag_start"]);
        if ($close == 1) {
            return "/" . $start . '(' . $name . ')\b(?>(?:(?!' . $end . ').)*)' . $end . "/is";
        }else{
            return "/" . $start . '(?:(' . $name . ')\b(?>(?:(?!' . $end . ').)*)|\/(' . $name . '))' . $end . "/is";
        }
    }

    public function convertTemp($name = '',$paths = ''){
        //$this->tplStart();
        if (empty($paths)) {
//            if (IS_WIN) {
//                if (strstr($name, '/')) {
//                    $name = str_replace('/', DS, $name);
//                }
//            }
            if (strstr($name, '@')) {
                $name = str_replace('@', DS, $name);
            }
            if (!strstr($name, '.')) {
                $name .= '.html';
            }
            $path = $this->tpl_path . $name;
        }else{
            $end = explode(DS,$name);
            if (!empty(end($end))){
                $end = end($end);
            }else{
                $end = $name;
            }
            $path = $paths . DS . $end . ".html";
        }
        $filename = $this->cache_path . md5($path) . ".html";
        if (!file_exists($filename) || filemtime($filename) < filemtime($path)) {
            if(!file_exists($path)) {
                throw new \Exception("模板 {$path} 不存在");
            }
            $content =  file_get_contents($path);
            $this->parseGlobalVar($content);
            $this->parseVar($content);
            $this->parseFuction($content);
            $content = $this->getMatch($content);
            file_put_contents($filename, $content);
            unset($content);
        }else{
            $content =  file_get_contents($path);
            $this->includeTag($content);
        }
        return $filename;
    }

    private function includeTag(&$content){
        $reg = $this->getRex(0,"include");
        $this->replaceAll($reg,$content,'file');
    }

    private function getMatch($content){
        foreach ($this->tag as $k => $v) {
            $name = $k;
            if (isset($v['alise'])){
                $name = explode(",",$v['alise']);
                $name = $k . "|".implode("|",$name);
            }
            $v["close"] = isset($v['close'])?$v['close']:1;
            $reg = $this->getRex($v['close'],$name);
            $attr = isset($v['attr'])?$v['attr']:'';
            $this->replaceAll($reg,$content,$attr);
        }
        return $content;
    }

    private function replaceAll($reg,&$content,$t_attr){
        $end = '';
        $arr = '';
        $name = '';
        $break = "####content####";
        if (preg_match($reg,$content)) {
            $content = preg_replace_callback($reg, function ($match) use (&$end,&$name,&$break,&$arr,$t_attr){
                if (isset($match[1]) && !empty($match[1])){
                    $name = $match[1];
                    $arr = $this->getP($match[0],$t_attr);
                    $function = $this->getTagconfig($name);
                    $data = call_user_func(array(new TplFunctions,$function),$arr,$break);
                    $data = explode($break,$data);
                    $end = end($data);
                    return $data[0];
                }elseif (!empty($name)){
                    return $end;
                }
            }, $content);
        }
    }

    private function getP($content = '',$name){
        $reg = "/\s+(?>(?P<name>[\w-]+)\s*)=(?>\s*)([\"'])(?P<value>(?:(?!\\2).)*)\\2/is";
        preg_match_all($reg,$content,$match);
        $match2 = [];
        foreach ($match['name'] as $key=>$v){
            $match2[$v] = $match['value'][$key];
        }
        unset($match);
        $temp = [];
        $name = explode(',',$name);
        array_map(function ($ke) use (&$temp,$match2){
            if (isset($match2[$ke])){
                $temp[$ke] = $match2[$ke];
            }
        },$name);
        unset($match2);
        return $temp;
    }
}